using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;

namespace BBCDiscTransfer
{
	static public class Extensions
	{
        /// <summary>
        /// Helper method to redraw the specified list view item.
        /// </summary>
        /// <param name="item"></param>
		static public void Refresh(this ListViewItem item)
		{
			item.ListView.RedrawItems(item.Index, item.Index, false);
		}

        /// <summary>
        /// Send the text command to the serial port.
        /// </summary>
        /// <param name="port">The port connected to the BBC micro.</param>
        /// <param name="command">The command to send.</param>
        /// <returns>Result of executing the command on the BBC micro.</returns>
		static public string SendCommand(this SerialPort port, string command)
		{
			port.ReadTimeout = 1000;
			port.DiscardOutBuffer();
			port.ReadExisting();
			port.Write("\r" + command + "\r");
			var result = port.ReadTo(">");
			port.ReadTimeout = 5000;
			result = port.ReadTo(">");
			return result;
		}

		/// <summary>
		/// Reads input from the serial port until it stops arriving.
		/// </summary>
		/// <param name="port">The port to read from.</param>
		/// <param name="timeOut">The time out.</param>
		/// <returns>A string containing the content.</returns>
		static public string ReadString(this SerialPort port, int timeOut = 1000)
		{
			var sb = new StringBuilder();
			var lastByteRead = DateTime.Now;
			// read until the inactivity > timeout
			while (DateTime.Now.Subtract(lastByteRead).TotalMilliseconds < timeOut)
			{
				// read all chars as they arrive
				while (port.BytesToRead > 0)
				{
					sb.Append(char.ConvertFromUtf32(port.ReadByte()));
					lastByteRead = DateTime.Now;
				}
			}
			return sb.ToString();
		}

		/// <summary>
		/// Sends the string and returns the result.
		/// </summary>
		/// <param name="port">The port to send the string to.</param>
		/// <param name="value">The value of the string to send.</param>
		/// <param name="timeOut">The time out.</param>
		/// <returns>A string containing the content received after the string was send.</returns>
		static public string SendString(this SerialPort port, string value, int timeOut = 1000)
		{
			port.ReadExisting();
			port.Write(value);
			return port.ReadString(timeOut);
		}

		/// <summary>
		/// Gets the named file over the serial port.
		/// </summary>
		/// <param name="port">The port.</param>
		/// <param name="file">The file.</param>
		/// <param name="progress">The progress.</param>
		/// <returns></returns>
		static public byte[] GetFile(this SerialPort port, string file, Action<int> progress)
		{
			var bytes = new List<byte>();
			var sb = new StringBuilder();
			port.ReadExisting();
			port.ReadTimeout = 1000;
			port.Write("\r");
			port.Write("*FX3,7\r");
			port.Write("CLOSE#0\r");
			port.Write(@"H%=OPENIN""" + file + @""":");
			port.Write("PRINT\">>>>\";:REPEAT VDUBGET#H%:UNTIL EOF#H%:CLOSE#H%\r");
			port.ReadTo(">>>>"); // the print statement itself echoed to the output
			port.ReadTo(">>>>"); // the print statement executed
			int count = 0;
			try
			{
				var b = port.ReadByte();
				while (b >= 0)
				{
					progress(count++);
					bytes.Add(Convert.ToByte(b));
					b = port.ReadByte();
					if (BBCDiscTransfer.MainForm.Abort)
					{
						// write escape and break
						port.Write(char.ConvertFromUtf32(27));
						break;
					}
				}
			}
			catch
			{
                // In the event of an error, return what we've got.
                // Should find a way to report it back to the caller while
                // leaving everything in a clean state, but was good enough
                // to get the programs I needed transferred at the time.
			}
			// bytes will include one extra character of the cursor
			bytes.RemoveAt(bytes.Count - 1);
			return bytes.ToArray();
		}
	}

}
